local skynet
if IS_SERVER_MODULE then

skynet = require "skynet"

end

function trans_table(tbl)
    if type(tbl) ~= "table" then
        return tbl
    end
    for key,value in pairs(tbl) do
        local num
        if type(key) == "string" then
            num = tonumber(key)
            if num then
                tbl[num] = trans_table(value)
                tbl[key] = nil
            end
        end
        if not num then
            tbl[key] = trans_table(value)
        end
    end
    return tbl
end

-- 查询数据库获取用户数据
function load_user(obj, uid)
    local ok,ret = skynet.call("MG_SAVE", "lua", "get_user", uid)
    if not ok then
        return false
    end
    load_schema(obj,ret)
    return true
end

-- db数据转换为schema对象
function load_schema(obj,db_data)
    if type(obj) ~= "table" or not obj.getSchema then
        if IS_SERVER_MODULE then
        skynet.error("[SCHEMA] load_schema error, obj invalid")
        else
        print("[SCHEMA] load_schema error, obj invalid")
        end
        return false
    end
    local schema = obj:getSchema()
    for key,value in pairs(db_data) do
        local sche = schema[key]
        if sche then
            if sche.typ == "dict" then
                local subList = {}
                for index,sObj in pairs(value) do
                    index = tonumber(index)
                    local subObj = sche.objTyp:new()
                    if load_schema(subObj,sObj) == false then
                        return false
                    end
                    subList[index] = subObj
                end
                obj[key] = subList
            else
                if sche.typ == "number" then
                    obj[key] = tonumber(value)
                else
                    obj[key] = value
                end
            end
        end
    end
end

-- 局部数据存数据库
--[[
	obj : 对象
	key : 字段名
	index : 
	new : 新对象 ? 
	nosave : 是否保存 	
]]
function save_schema(obj,key,index,new,nosave)
    local root = obj
    local err
    local sub_save_schema
    local function check()
        if type(obj) == "table" and obj.getSchema then
            local schema = obj:getSchema()
            if key then
                local typ = type(new)
                if schema[key].typ == "dict" then
                    if typ == 'nil' or typ == "table" then
                        return
                    end
                    return string.format("[SCHEMA] error !! key %s , schema %s , get %s", key, schema[key].typ, typ)
                end
                if schema[key].typ ~= typ then
                    return string.format("[SCHEMA] error !! key %s , schema %s , get %s", key, schema[key].typ, typ)
                end
            end
        else
            return "[SCHEMA] error !! obj has no schema"
        end
    end 
    local function check_schema(obj,key,index,new)
        err = check()
        if err then
            skynet.error(debug.traceback())
            return false
        end
        return true
    end

    sub_save_schema = function(obj,key,index,new)
        if err then
            return err
        end  
        if check_schema(obj,key,index,new) == false then
            return err
        end
        if key and not index and new then   -- 没有index的key value情况
            if type(new) == "table" and new.getSchema then
                return { [key] = sub_save_schema(new,nil,nil,nil)}
            else
                return { [key] = new }
            end
        end
        if key and index and new then       -- 修改某个index情况
            return { [key] = {[index] = sub_save_schema(new,nil,nil,nil)} }
        end
        if not key and not index and not new then   -- 迭代自身整个schema
            if obj:getSchema() then
                local ret = {}
                for key, sche in pairs(obj:getSchema()) do
                    if sche.typ == "dict" then
                        local subRet = {}
                        for index, subObj in pairs(obj[key]) do
                            subRet[""..index] = sub_save_schema(subObj,nil,nil,nil)
                        end
                        ret[key] = subRet
                    else
                        ret[key] = obj[key]
                    end
                end
                return ret
            else
                return obj
            end
        end
    end

    local save = sub_save_schema(obj,key,index,new)
    if type(save) == "string" then
        skynet.error(err)
        return false
    end
    if nosave then
        return save
    end
    if root.class == user_t then
        -- delete case
        if key and not new then
            if index then
                --skynet.call("MG_SAVE", "lua", "unset_user", obj.uid, key, index)
            end
        else
            skynet.call("MG_SAVE", "lua", "set_user", obj.uid, key, index, save)
        end
    end
    return true
end

-- 绑定schema表到对象，并设置getter和setter方法
function set_schema(args)
    local obj = args.obj
    local schema = args.schema
	-- 内置方法getSchema(返回schema,定义对象而已)
    obj.getSchema = function() return schema end
    local on_set = nil
    if args.on_set then
        on_set = args.on_set
    end
    for key, sche in pairs(schema) do
        obj[key] = clone(sche.default)

        local getName = "get_"..key
        local setName = "set_"..key
        obj[getName] = function(obj) 
            return obj[key]
        end
        if IS_SERVER_MODULE then
			if sche.typ == "dict" then
				obj[setName] = function(obj,index,new)
					if type(index) ~= "number" then
						skynet.error(string.format("[SCHEMA] error !! key %s index must be number", key))
						return
					end
					obj[key][index] = new
					if save_schema(obj,key,index,new) then
						if sche.sync and on_set then
							on_set(obj,key,index,new)
						end
					end
				end
			else
				obj[setName] = function(obj,new)
					obj[key] = new
					if save_schema(obj,key,nil,new) then
						if sche.sync and on_set then
							on_set(obj,key,nil,new)
						end
					end
				end
			end

        end
    end
end

-- 抽离需要同步到客户端的数据
function sync_schema(obj)
    if type(obj) ~= "table" or not obj.getSchema then
        return obj
    end
    local ret = {}
    local schema = obj:getSchema()
    for key,sche in pairs(schema) do
        if sche.sync ~= false then
            if sche.typ == "dict" then
                local subRet = {}
                for index, subObj in pairs(obj[key]) do
                    subRet[index] = sync_schema(subObj)
                end
                ret[key] = subRet
            else
                ret[key] = obj[key]
            end
        end
    end
    return ret
end
